/*
  AHT10 - A Humidity Library for Arduino.

  Supported Sensor modules:
    AHT10-Breakout Module - https://www.aliexpress.com/item/33002710848.html

  Created by Thinary Eletronic at Modern Device on April 2019.
 
 * This file is part of Thinary_AHT10.
 *
 * Thinary_AHT10 is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation, either version 3 of
 * the License, or(at your option) any later version.
 *
 * Thinary_AHT10 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Thinary_AHT10.  If not, see
 * <http://www.gnu.org/licenses/>.
 */

#include <stdint.h>
#include <math.h>
#include "AHT10.h"

// Specify the constants for water vapor and barometric pressure.
#define WATER_VAPOR 17.62f
#define BAROMETRIC_PRESSURE 243.5f

// IIC常数
uint8_t const IIC_READ = 1;
uint8_t const IIC_WRITE = 0;
uint8_t const IIC_DELAY_USEC = 4;

Sensor_CMD eSensorCalibrateCmd[3] = {0xE1, 0x08, 0x00};
Sensor_CMD eSensorNormalCmd[3] = {0xA8, 0x00, 0x00};
Sensor_CMD eSensorMeasureCmd[3] = {0xAC, 0x33, 0x00};
Sensor_CMD eSensorResetCmd = 0xBA;
boolean GetRHumidityCmd = true;
boolean GetTempCmd = false;

/******************************************************************************
 * Global Functions
 ******************************************************************************/
AHT10Class::AHT10Class(uint8_t sdaPin, uint8_t sclPin)
{
    // 设置SDA和SCL引脚
    SCL_pin = sclPin;
    SDA_pin = sdaPin;
}

boolean AHT10Class::begin(unsigned char _AHT10_address)
{
    AHT10_address = _AHT10_address;
    // IIC开始
    IICbegin(SDA_pin, SCL_pin);
    // IIC开始传输
    IICbeginTransmission(AHT10_address);
    // IIC写入3个bit
    IICwrite(eSensorCalibrateCmd, 3);
    // IIC结束传输
    IICendTransmission();
    delay(500);
    if ((readStatus() & 0x68) == 0x08)
        return true;
    else
    {
        return false;
    }
}

/**********************************************************
 * GetHumidity
 *  Gets the current humidity from the sensor.
 *
 * @return float - The relative humidity in %RH
 **********************************************************/
float AHT10Class::GetHumidity(void)
{
    float value = readSensor(GetRHumidityCmd);
    if (value == 0)
    {
        return 0; // Some unrealistic value
    }
    return value * 100 / 1048576;
}

/**********************************************************
 * GetTemperature
 *  Gets the current temperature from the sensor.
 *
 * @return float - The temperature in Deg C
 **********************************************************/
float AHT10Class::GetTemperature(void)
{
    float value = readSensor(GetTempCmd);
    return ((200 * value) / 1048576) - 50;
}

/**********************************************************
 * GetDewPoint
 *  Gets the current dew point based on the current humidity and temperature
 *
 * @return float - The dew point in Deg C
 **********************************************************/
float AHT10Class::GetDewPoint(void)
{
    float humidity = GetHumidity();
    float temperature = GetTemperature();

    // Calculate the intermediate value 'gamma'
    float gamma = log(humidity / 100) + WATER_VAPOR * temperature / (BAROMETRIC_PRESSURE + temperature);
    // Calculate dew point in Celsius
    float dewPoint = BAROMETRIC_PRESSURE * gamma / (WATER_VAPOR - gamma);

    return dewPoint;
}

/******************************************************************************
 * Private Functions
 ******************************************************************************/

unsigned long AHT10Class::readSensor(boolean GetDataCmd)
{
    unsigned long result, temp[6];

    IICbeginTransmission(AHT10_address);
    IICwrite(eSensorMeasureCmd, 3);
    IICendTransmission();
    delay(100);

    IICrequestFrom(AHT10_address, (uint8_t)6);

    for (unsigned char i = 0; IICavailable() > 0; i++)
    {
        temp[i] = IICread2();
    }

    if (GetDataCmd)
    {
        result = ((temp[1] << 16) | (temp[2] << 8) | temp[3]) >> 4;
    }
    else
    {
        result = ((temp[3] & 0x0F) << 16) | (temp[4] << 8) | temp[5];
    }

    return result;
}

unsigned char AHT10Class::readStatus(void)
{
    unsigned char result = 0;

    IICrequestFrom(AHT10_address, (uint8_t)1);
    result = IICread2();
    return result;
}

void AHT10Class::Reset(void)
{
    IICbeginTransmission(AHT10_address);
    IICwrite(eSensorResetCmd);
    IICendTransmission();
    delay(20);
}

// AHT10IIC
// 将SDA和SCL都调为高电平
void AHT10Class::IICbegin(uint8_t sdapin, uint8_t sclpin)
{
    // 设置缓冲区
    rxBufferIndex = 0;
    rxBufferLength = 0;
    // 指定是否在传输状态
    transmitting = false;
    // 错误类型
    error = 0;
    // 设置引脚
    SDA_pin = sdapin;
    pinMode(SDA_pin, OUTPUT);
    digitalWrite(SDA_pin, HIGH);
    SCL_pin = sclpin;
    pinMode(SCL_pin, OUTPUT);
    digitalWrite(SCL_pin, HIGH);
}

// 开始传输
void AHT10Class::IICbeginTransmission(uint8_t address)
{
    if (transmitting)
    {
        IICrestart();
        // 写入地址
        // 清除读标志位
        error = IICwrite((address << 1) | IIC_WRITE) ? 0 : 2; // clr read bit
    }
    else
    {
        IICstart();
        // 写入地址
        // 清除读标志位
        error = IICwrite((address << 1) | IIC_WRITE) ? 0 : 2; // clr read bit
    }
    transmitting = 1;
}

void AHT10Class::IICbeginTransmission(int address)
{
    IICbeginTransmission((uint8_t)address);
}

// 结束传输
uint8_t AHT10Class::IICendTransmission(uint8_t sendStop)
{
    uint8_t transError = error;
    if (sendStop)
    {
        IICstop();
        transmitting = 0;
    }
    error = 0;
    return transError;
}

uint8_t AHT10Class::IICendTransmission(void)
{
    return IICendTransmission(true);
}

// 现将SDA设置为低电平，再将SCL设置为低电平
void AHT10Class::IICstart(void)
{
    digitalWrite(SDA_pin, LOW);
    delayMicroseconds(IIC_DELAY_USEC);
    digitalWrite(SCL_pin, LOW);
}

// 重新开始，将SDA和SCL都设置为高电平
void AHT10Class::IICrestart(void)
{
    digitalWrite(SDA_pin, HIGH);
    digitalWrite(SCL_pin, HIGH);
    delayMicroseconds(IIC_DELAY_USEC);
    IICstart();
}

// SDA产生一个上升沿，SCL设置高电平
void AHT10Class::IICstop(void)
{
    digitalWrite(SDA_pin, LOW);
    delayMicroseconds(IIC_DELAY_USEC);
    digitalWrite(SCL_pin, HIGH);
    delayMicroseconds(IIC_DELAY_USEC);
    digitalWrite(SDA_pin, HIGH);
    delayMicroseconds(IIC_DELAY_USEC);
}

// 读取数据
uint8_t AHT10Class::IICread(uint8_t last)
{
    uint8_t b = 0;
    // make sure pull-up enabled
    digitalWrite(SDA_pin, HIGH);
    pinMode(SDA_pin, INPUT);
    // read byte
    for (uint8_t i = 0; i < 8; i++)
    {
        // don't change this loop unless you verify the change with a scope
        b <<= 1;
        delayMicroseconds(IIC_DELAY_USEC);
        digitalWrite(SCL_pin, HIGH);
        if (digitalRead(SDA_pin))
        {
            b |= 1;
        }
        //else b &= 0;
        digitalWrite(SCL_pin, LOW);
    }
    // send Ack or Nak
    pinMode(SDA_pin, OUTPUT);
    digitalWrite(SDA_pin, last);
    digitalWrite(SCL_pin, HIGH);
    delayMicroseconds(IIC_DELAY_USEC);
    digitalWrite(SCL_pin, LOW);
    digitalWrite(SDA_pin, LOW);
    return b;
}

//------------------------------------------------------------------------------
/**
 * Write a byte.
 *
 * \param[in] data The byte to send.
 *
 * \return The value true, 1, if the slave returned an Ack or false for Nak.
 */
bool AHT10Class::IICwrite(uint8_t data)
{
    // write byte
    for (uint8_t m = 0X80; m != 0; m >>= 1)
    {
        // don't change this loop unless you verify the change with a scope
        digitalWrite(SDA_pin, m & data);
        digitalWrite(SCL_pin, HIGH);
        delayMicroseconds(IIC_DELAY_USEC);
        digitalWrite(SCL_pin, LOW);
    }
    // get Ack or Nak
    pinMode(SDA_pin, INPUT);
    // enable pullup
    // digitalWrite(SDA_pin, HIGH);
    digitalWrite(SCL_pin, HIGH);
    uint8_t rtn = digitalRead(SDA_pin);
    digitalWrite(SCL_pin, LOW);
    pinMode(SDA_pin, OUTPUT);
    digitalWrite(SDA_pin, LOW);
    return rtn == 0;
}

// must be called in:
// slave tx event callback
// or after beginTransmission(address)
void AHT10Class::IICwrite(uint8_t *data, uint8_t quantity)
{
    for (uint8_t i = 0; i < quantity; ++i)
    {
        IICwrite(data[i]);
    }
}

uint8_t AHT10Class::IICrequestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize, uint8_t sendStop)
{
    uint8_t localerror = 0;
    if (isize > 0)
    {
        // send internal address; this mode allows sending a repeated start to access
        // some devices' internal registers. This function is executed by the hardware
        // TWI module on other processors (for example Due's TWI_IADR and TWI_MMR registers)
        IICbeginTransmission(address);
        // the maximum size of internal address is 3 bytes
        if (isize > 3)
        {
            isize = 3;
        }
        // write internal register address - most significant byte first
        while (isize-- > 0)
            IICwrite((uint8_t)(iaddress >> (isize * 8)));
        IICendTransmission(false);
    }
    // clamp to buffer length
    if (quantity > BUFFER_LENGTH)
    {
        quantity = BUFFER_LENGTH;
    }
    if (transmitting)
    {
        IICrestart();
        localerror = IICwrite((address << 1) | IIC_READ);
    }
    else
    {
        IICstart();
        localerror = IICwrite((address << 1) | IIC_READ);
    }
    if (error == 0 && localerror)
        error = 2;
    // perform blocking read into buffer
    for (uint8_t cnt = 0; cnt < quantity; cnt++)
        rxBuffer[cnt] = IICread(cnt == quantity - 1);
    // set rx buffer iterator vars
    rxBufferIndex = 0;
    rxBufferLength = quantity;
    if (sendStop)
    {
        transmitting = 0;
        IICstop();
    }
    return quantity;
}

uint8_t AHT10Class::IICrequestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop)
{
    return IICrequestFrom((uint8_t)address, (uint8_t)quantity, (uint32_t)0, (uint8_t)0, (uint8_t)sendStop);
}

uint8_t AHT10Class::IICrequestFrom(int address, int quantity, int sendStop)
{
    return IICrequestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)sendStop);
}

uint8_t AHT10Class::IICrequestFrom(uint8_t address, uint8_t quantity)
{
    return IICrequestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t) true);
}

uint8_t AHT10Class::IICrequestFrom(int address, int quantity)
{
    return IICrequestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t) true);
}

int AHT10Class::IICavailable(void)
{
    return rxBufferLength - rxBufferIndex;
}

int AHT10Class::IICread2(void)
{
    int value = -1;
    if (rxBufferIndex < rxBufferLength)
    {
        value = rxBuffer[rxBufferIndex];
        ++rxBufferIndex;
    }
    return value;
}

int AHT10Class::IICpeek(void)
{
    int value = -1;

    if (rxBufferIndex < rxBufferLength)
    {
        value = rxBuffer[rxBufferIndex];
    }
    return value;
}